package com.andy.qi.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimerTask;

import com.andy.qi.domain.nogame.NoAction;
import com.andy.qi.domain.nogame.NoGame;
import com.andy.qi.domain.nogame.NoHistory;
import com.andy.qi.domain.nogame.NoHistorySeat;
import com.andy.qi.domain.nogame.NoSeat;
import com.andy.qi.domain.nogame.NoTing;
import com.andy.qi.domain.room.Room;
import com.andy.qi.domain.room.RoomDr;
import com.andy.qi.domain.room.Seat;
import com.andy.qi.util.Json;
import com.andy.qi.util.ListUtils;
import com.andy.qi.util.StrUtils;

public class GameOfXzdd extends GameMgr {
    
    
    public GameOfXzdd(RoomMgr roomMgr) {
        super(roomMgr);
    }
    

    public int mopai(NoGame game, int seatIndex) {
        if(game.currentIndex == game.mahjongs.length){
            return -1;
        }
        NoSeat data = game.gameSeats[seatIndex];
        List<Integer> mahjongs = data.holds;
        int pai = game.mahjongs[game.currentIndex];
        mahjongs.add(pai);

        //统计牌的数目 ，用于快速判定（空间换时间）
        Integer c = data.countMap.get(pai);
        if(c == null) {
            c = 0;
        }
        data.countMap.put(pai, c + 1);
        game.currentIndex ++;
        return pai;
    }

    public void deal(NoGame game){
        //强制清0
        game.currentIndex = 0;

        //每人13张 一共 13*4 ＝ 52张 庄家多一张 53张
        int seatIndex = game.button;
        for(int i = 0; i < 52; ++i){
            List<Integer> mahjongs = game.gameSeats[seatIndex].holds;
            if(mahjongs == null){
                mahjongs = new ArrayList<>();
                game.gameSeats[seatIndex].holds = mahjongs;
            }
            mopai(game,seatIndex);
            seatIndex ++;
            seatIndex %= 4;
        }

        //庄家多摸最后一张
        mopai(game,game.button);
        //当前轮设置为庄家
        game.turn = game.button;
    }

    //检查是否可以碰
    public void checkCanPeng(NoGame game, NoSeat seatData, int targetPai) {
        if(getMJType(targetPai) == seatData.que){
            return;
        }
        Integer count = seatData.countMap.get(targetPai);
        if(count != null && count >= 2){
            seatData.canPeng = true;
        }
    }

    //检查是否可以点杠
    public void checkCanDianGang(NoGame game, NoSeat seatData, int targetPai){
        //检查玩家手上的牌
        //如果没有牌了，则不能再杠
        if(game.mahjongs.length <= game.currentIndex){
            return;
        }
        if(getMJType(targetPai) == seatData.que){
            return;
        }
        Integer count = seatData.countMap.get(targetPai);
        if(count != null && count >= 3){
            seatData.canGang = true;
            seatData.gangPai.add(targetPai);
            return;
        }
    }

    //检查是否可以暗杠
    public void checkCanAnGang(NoGame game, NoSeat seatData){
        //如果没有牌了，则不能再杠
        if(game.mahjongs.length <= game.currentIndex){
            return;
        }

        for(Integer pai : seatData.countMap.keySet()){
            if(getMJType(pai) != seatData.que){
                Integer c = seatData.countMap.get(pai);
                if(c != null && c == 4){
                    seatData.canGang = true;
                    seatData.gangPai.add(pai);
                }
            }
        }
    }

    //检查是否可以弯杠(自己摸起来的时候)
    public void checkCanWanGang(NoGame game, NoSeat seatData){
        //如果没有牌了，则不能再杠
        if(game.mahjongs.length <= game.currentIndex){
            return;
        }

        //从碰过的牌中选
        for(int i = 0; i < seatData.pengs.size(); ++i){
            Integer pai = seatData.pengs.get(i);
            if(seatData.countMap.get(pai) == 1){
                seatData.canGang = true;
                seatData.gangPai.add(pai);
            }
        }
    }

    public void checkCanHu(NoGame game, NoSeat seatData,int targetPai) {
        game.lastHuPaiSeat = -1;
        if(getMJType(targetPai) == seatData.que){
            return;
        }
        seatData.canHu = false;
        for(Integer k : seatData.tingMap.keySet()){
            if(targetPai == k){
                seatData.canHu = true;
            }
        }
    }
    
    private void fnClear(NoSeat sd){
        sd.canPeng = false;
        sd.canGang = false;
        sd.gangPai = new ArrayList<>();
        sd.canHu = false;
        sd.lastFangGangSeat = -1;    
    }
    
    public void clearAllOptions(NoGame game, NoSeat seatData){
        if(seatData != null){
            fnClear(seatData);
        } else {
            game.qiangGangContext = null;
            for(int i = 0; i < game.gameSeats.length; ++i){
                fnClear(game.gameSeats[i]);
            }
        }
    }

    //检查听牌
    public void checkCanTingPai(NoGame game, NoSeat seatData){
        seatData.tingMap = new HashMap<>();
        
        //检查手上的牌是不是已打缺，如果未打缺，则不进行判定
        for(int i = 0; i < seatData.holds.size(); ++i){
            int pai = seatData.holds.get(i);
            if(getMJType(pai) == seatData.que){
                return;
            }   
        }

        //检查是否是七对 前提是没有碰，也没有杠 ，即手上拥有13张牌
        if(seatData.holds.size() == 13){
            //有5对牌
            boolean hu = false;
            int danPai = -1;
            int pairCount = 0;
            for(Integer k : seatData.countMap.keySet()){
                int c = seatData.countMap.get(k);
                if( c == 2 || c == 3){
                    pairCount++;
                }
                else if(c == 4){
                    pairCount += 2;
                }

                if(c == 1 || c == 3){
                    //如果已经有单牌了，表示不止一张单牌，并没有下叫。直接闪
                    if(danPai >= 0){
                        break;
                    }
                    danPai = k;
                }
            }

            //检查是否有6对 并且单牌是不是目标牌
            if(pairCount == 6){
                //七对只能和一张，就是手上那张单牌
                //七对的番数＝ 2番+N个4个牌（即龙七对）
                seatData.tingMap.put(danPai, new NoTing(2, "7pairs"));
                //如果是，则直接返回咯
            }
        }

        //检查是否是对对胡  由于四川麻将没有吃，所以只需要检查手上的牌
        //对对胡叫牌有两种情况
        //1、N坎 + 1张单牌
        //2、N-1坎 + 两对牌
        int singleCount = 0;
        int colCount = 0;
        int pairCount = 0;
        List<Integer> arr = new ArrayList<>();
        for(int k : seatData.countMap.keySet()){
            int c = seatData.countMap.get(k);
            if(c == 1){
                singleCount++;
                arr.add(k);
            }
            else if(c == 2){
                pairCount++;
                arr.add(k);
            }
            else if(c == 3){
                colCount++;
            }
            else if(c == 4){
                //手上有4个一样的牌，在四川麻将中是和不了对对胡的 随便加点东西
                singleCount++;
                pairCount+=2;
            }
        }

        if((pairCount == 2 && singleCount == 0) || (pairCount == 0 && singleCount == 1) ){
            for(int i = 0; i < arr.size(); ++ i){
                //对对胡1番
                Integer p = arr.get(i);
                if(seatData.tingMap.get(p) == null){
                    seatData.tingMap.put(p, new NoTing(1, "duidui"));
                }
            }
        }

        //System.out.println(seatData.holds);
        //System.out.println(seatData.countMap);
        //System.out.println("singleCount:" + singleCount + ",colCount:" + colCount + ",pairCount:" + pairCount);
        //检查是不是平胡
        if(seatData.que != 0){
            mjutils.checkTingPai(seatData,0,9);        
        }

        if(seatData.que != 1){
            mjutils.checkTingPai(seatData,9,18);        
        }

        if(seatData.que != 2){
            mjutils.checkTingPai(seatData,18,27);        
        }
    }


    public boolean hasOperations(NoSeat seatData){
        if(seatData.canGang || seatData.canPeng || seatData.canHu){
            return true;
        }
        return false;
    }

    public void sendOperations(NoGame game, NoSeat seatData, int pai) {
        if(hasOperations(seatData)){
            if(pai == -1){
                pai = seatData.holds.get(seatData.holds.size() - 1);
            }
            
            Map<String, Object> data = new HashMap<>();
            data.put("pai", pai);
            data.put("hu", seatData.canHu);
            data.put("peng", seatData.canPeng);
            data.put("gang", seatData.canGang);
            data.put("gangpai", seatData.gangPai);

            //如果可以有操作，则进行操作
            userMgr.sendMsg(seatData.userId, "game_action_push", data);

            data.put("si", seatData.seatIndex);
        }
        else{
            userMgr.sendMsg(seatData.userId,"game_action_push", null);
        }
    }

    public void moveToNextUser(NoGame game, Integer nextSeat){
        game.fangpaoshumu = 0;
        //找到下一个没有和牌的玩家
        if(nextSeat == null){
            while(true){
                game.turn ++;
                game.turn %= 4;
                NoSeat turnSeat = game.gameSeats[game.turn];
                if(turnSeat.hued == false){
                    return;
                }
            }
        }
        else{
            game.turn = nextSeat;
        }
    }

    public void doUserMoPai(NoGame game){
        game.chuPai = -1;
        NoSeat turnSeat = game.gameSeats[game.turn];
        turnSeat.lastFangGangSeat = -1;
        turnSeat.guoHuFan = -1;
        int pai = mopai(game,game.turn);
        //牌摸完了，结束
        if(pai == -1){
            doGameOver(game,turnSeat.userId, false);
            return;
        }
        else{
            int numOfMJ = game.mahjongs.length - game.currentIndex;
            userMgr.broacastInRoom("mj_count_push",numOfMJ,turnSeat.userId,true);
        }

        recordGameAction(game,game.turn,ACTION_MOPAI,pai);

        //通知前端新摸的牌
        userMgr.sendMsg(turnSeat.userId,"game_mopai_push",pai);
        //检查是否可以暗杠或者胡
        //检查胡，直杠，弯杠
        checkCanAnGang(game,turnSeat);
        checkCanWanGang(game,turnSeat);

        //检查看是否可以和
        checkCanHu(game,turnSeat,pai);

        //广播通知玩家出牌方
        turnSeat.canChuPai = true;
        userMgr.broacastInRoom("game_chupai_push",turnSeat.userId,turnSeat.userId,true);

        //通知玩家做对应操作
        sendOperations(game,turnSeat,game.chuPai);
    }


    public boolean isMenQing(NoSeat gameSeatData){
        return (gameSeatData.pengs.size() + gameSeatData.wangangs.size() + gameSeatData.diangangs.size()) == 0;
    }
    
    private boolean isZhongZhangFn(List<Integer> arr){
        for(int i = 0; i < arr.size(); ++i){
            int pai = arr.get(i);
            if(pai == 0 || pai == 8 || pai == 9 || pai == 17 || pai == 18 || pai == 26){
                return false;
            }
        }
        return true;
    }
    
    public boolean isZhongZhang(NoSeat gameSeatData){
        if(isZhongZhangFn(gameSeatData.pengs) == false){
            return false;
        }
        if(isZhongZhangFn(gameSeatData.angangs) == false){
            return false;
        }
        if(isZhongZhangFn(gameSeatData.diangangs) == false){
            return false;
        }
        if(isZhongZhangFn(gameSeatData.wangangs) == false){
            return false;
        }
        if(isZhongZhangFn(gameSeatData.holds) == false){
            return false;
        }
        return true;
    }

    private boolean isJiangDuiFn(List<Integer> arr){
        for(int i = 0; i < arr.size(); ++i){
            int pai = arr.get(i);
            if(pai != 1 && pai != 4 && pai != 7
                    && pai != 9 && pai != 13 && pai != 16
                    && pai != 18 && pai != 21 && pai != 25
                    ){
                return false;
            }
        }
        return true;
    }
    
    public boolean isJiangDui(NoSeat gameSeatData){
        if(isJiangDuiFn(gameSeatData.pengs) == false){
            return false;
        }
        if(isJiangDuiFn(gameSeatData.angangs) == false){
            return false;
        }
        if(isJiangDuiFn(gameSeatData.diangangs) == false){
            return false;
        }
        if(isJiangDuiFn(gameSeatData.wangangs) == false){
            return false;
        }
        if(isJiangDuiFn(gameSeatData.holds) == false){
            return false;
        }
        return true;
    }

    public boolean isTinged(NoSeat seatData){
        return !seatData.tingMap.isEmpty();
    }

    public int computeFanScore(NoGame game, int fan){
        if(fan > game.conf.maxFan){
            fan = game.conf.maxFan;
        }
        return (1 << fan) * game.conf.baseScore;
    }

    //是否需要查大叫(有两家以上未胡，且有人没有下叫)
    public boolean needChaDaJiao(NoGame game){
        //查叫
        int numOfHued = 0;
        int numOfTinged = 0;
        int numOfUntinged = 0;
        for(int i = 0; i < game.gameSeats.length; ++i){
            NoSeat ts = game.gameSeats[i];
            if(ts.hued){
                numOfHued ++;
                numOfTinged++;
            }
            else if(isTinged(ts)){
                numOfTinged++;
            }
            else{
                numOfUntinged++;
            }
        }
       
        //如果三家都胡牌了，不需要查叫
        if(numOfHued == 3){
            return false;
        }
        
        //如果没有任何一个人叫牌，也没有任何一个胡牌，则不需要查叫
        if(numOfTinged == 0){
            return false;
        }
        
        //如果都听牌了，也不需要查叫
        if(numOfUntinged == 0){
            return false;
        }
        return true;
    }

    public NoTing findMaxFanTingPai(NoSeat ts){
        //找出最大番
        NoTing cur = null;
        for(Integer k : ts.tingMap.keySet()){
            NoTing tpai = ts.tingMap.get(k);
            if(cur == null || tpai.fan > cur.fan){
                cur = tpai;
            }
        }
        return cur;
    }

    public List<Integer> findUnTingedPlayers(NoGame game){
        List<Integer> arr = new ArrayList<>();
        for(int i = 0; i < game.gameSeats.length; ++i){
            NoSeat ts = game.gameSeats[i];
            //如果没有胡，且没有听牌
            if(!ts.hued && !isTinged(ts)){
                arr.add(i);
                recordUserAction(game,ts,"beichadajiao",-1);
            }
        }
        return arr;
    }

    public void chaJiao(NoGame game){
        List<Integer> arr = findUnTingedPlayers(game);
        for(int i = 0; i < game.gameSeats.length; ++i){
            NoSeat ts = game.gameSeats[i];
            //如果没有胡，但是听牌了，则未叫牌的人要给钱
            if(!ts.hued && isTinged(ts)){
                NoTing cur = findMaxFanTingPai(ts);
                ts.fan = cur.fan;
                ts.pattern = cur.pattern;
                recordUserAction(game,ts,"chadajiao",arr);
            }
        }
    }

    public void calculateResult(NoGame game, Room roomInfo){
        boolean isNeedChaDaJia = needChaDaJiao(game);
        if(isNeedChaDaJia){
            chaJiao(game);
        }
        
        int baseScore = game.conf.baseScore;
        int numOfHued = 0;
        for(int i = 0; i < game.gameSeats.length; ++i){
            if(game.gameSeats[i].hued == true){
                numOfHued++;
            }
        }
        
        for(int i = 0; i < game.gameSeats.length; ++i){
            NoSeat sd = game.gameSeats[i];
            
            //统计杠的数目
            sd.numAnGang = sd.angangs.size();
            sd.numMingGang = sd.wangangs.size() + sd.diangangs.size();
            
            //对所有胡牌的玩家进行统计
            if(isTinged(sd)){
                //统计自己的番子和分数
                //基础番(平胡0番，对对胡1番、七对2番) + 清一色2番 + 杠+1番
                //杠上花+1番，杠上炮+1番 抢杠胡+1番，金钩胡+1番，海底胡+1番
                int fan = sd.fan;
                if(isQingYiSe(sd)){
                    sd.qingyise = true;
                    fan += 2;
                }
                
                int numOfGangs = sd.diangangs.size() + sd.wangangs.size() + sd.angangs.size();
                for(int j = 0; j < sd.pengs.size(); ++j){
                    Integer pai = sd.pengs.get(j);
                    if(sd.countMap.get(pai) == 1){
                        numOfGangs++;
                    }
                }
                for(Integer k : sd.countMap.keySet()){
                    if(sd.countMap.get(k).intValue() == 4){
                        numOfGangs++;
                    }
                }
                sd.numofgen = numOfGangs;
                
                //金钩胡
                if(sd.holds.size() == 1 || sd.holds.size() == 2){
                    fan += 1;
                    sd.isJinGouHu = true;
                }
                
                if(sd.isHaiDiHu){
                    fan += 1;
                }
                
                if(game.conf.tiandihu){
                    if(sd.isTianHu){
                        fan += 3;
                    }
                    else if(sd.isDiHu){
                        fan += 2;
                    }
                }
                
                boolean isjiangdui = false;
                if(game.conf.jiangdui){
                    if("7pairs".equals(sd.pattern)){
                        if(sd.numofgen > 0){
                            sd.numofgen -= 1;
                            sd.pattern = "l7pairs";
                            isjiangdui = isJiangDui(sd);
                            if(isjiangdui){
                                sd.pattern = "j7paris";
                                fan += 2;    
                            }   
                            else{
                                fan += 1;
                            }
                        }
                    }
                    else if("duidui".equals(sd.pattern)){
                        isjiangdui = isJiangDui(sd);
                        if(isjiangdui){
                            sd.pattern = "jiangdui";
                            fan += 2;   
                        }
                    }   
                }
                
                if(game.conf.menqing){
                    //不是将对，才检查中张
                    if(!isjiangdui){
                        sd.isZhongZhang = isZhongZhang(sd);
                        if(sd.isZhongZhang){
                            fan += 1;
                        }                
                    }
                    
                    sd.isMenQing = isMenQing(sd);
                    if(sd.isMenQing){
                        fan += 1;
                    }                
                }
                
                fan += sd.numofgen;
                if(sd.isGangHu){
                    fan += 1;
                }
                if(sd.isQiangGangHu){
                    fan += 1;
                }

                //收杠钱
                int additonalscore = 0;
                for(int a = 0; a < sd.actions.size(); ++a){
                    NoAction ac = sd.actions.get(a);
                    if("fanggang".equals(ac.type)){
                        NoSeat ts = game.gameSeats[ac.targets.get(0)];
                        //检查放杠的情况，如果目标没有和牌，且没有叫牌，则不算 用于优化前端显示
                        if(isNeedChaDaJia && (ts.hued) == false && (isTinged(ts) == false)){
                            ac.state = "nop";
                        }
                    }
                    else  if("angang".equals(ac.type)  || "wangang".equals(ac.type) || "diangang".equals(ac.type)) {
                        if(ac.state != "nop"){
                            int acscore = ac.score;
                            additonalscore += ac.targets.size() * acscore * baseScore;
                            //扣掉目标方的分
                            for(int t = 0; t < ac.targets.size(); ++t){
                                int six = ac.targets.get(t);
                                game.gameSeats[six].score -= acscore * baseScore;
                            }                   
                        }
                    }
                    else if("maozhuanyu".equals(ac.type)){
                        //对于呼叫转移，如果对方没有叫牌，表示不得行
                        if(isTinged(ac.owner)){
                            //如果
                            NoAction ref = ac.ref;
                            int acscore = ref.score;
                            int total = ref.targets.size() * acscore * baseScore;
                            additonalscore += total;
                            //扣掉目标方的分
                            if(ref.payTimes == 0){
                                for(int t = 0; t < ref.targets.size(); ++t){
                                    int six = ref.targets.get(t);
                                    game.gameSeats[six].score -= acscore * baseScore;
                                }                            
                            }
                            else{
                                //如果已经被扣过一次了，则由杠牌这家赔
                                ac.owner.score -= total;
                            }
                            ref.payTimes++;
                            ac.owner = null;
                            ac.ref = null;
                        }
                    }
                    else if(ac.type == "zimo" || ac.type == "hu" || ac.type == "ganghua" || ac.type == "dianganghua" || ac.type == "gangpaohu" || ac.type == "qiangganghu" || ac.type == "chadajiao"){
                        int extraScore = 0;
                        if(ac.iszimo){
                            if(game.conf.zimo == 0){
                                //自摸加底
                                extraScore = baseScore;
                            }
                            if(game.conf.zimo == 1){
                                fan += 1;
                            }
                            else{
                                //nothing.
                            }
                            sd.numZiMo ++;
                        }
                        else{
                            if(ac.type != "chadajiao"){
                                sd.numJiePao ++;
                            }
                        }
                        
                        int score = computeFanScore(game,fan) + extraScore;
                        sd.score += score * ac.targets.size();

                        for(int t = 0; t < ac.targets.size(); ++t){
                            int six = ac.targets.get(t);
                            NoSeat td = game.gameSeats[six]; 
                            td.score -= score;
                            if(td != sd){
                                if(ac.type == "chadajiao"){
                                    td.numChaJiao ++;
                                }
                                else if(!ac.iszimo){
                                    td.numDianPao ++;
                                }                            
                            }
                        }
                    }
                }

                if(fan > game.conf.maxFan){
                    fan = game.conf.maxFan;
                }
                //一定要用 += 。 因为此时的sd.score可能是负的
                sd.score += additonalscore;
                if(sd.pattern != null){
                    sd.fan = fan;
                }
            }
            else{
                for(int a = sd.actions.size() -1; a >= 0; --a){
                    NoAction ac = sd.actions.get(a);
                    if("angang".equals(ac.type)  || "wangang".equals(ac.type) || "diangang".equals(ac.type)) {
                        //如果3家都胡牌，则需要结算。否则认为是查叫
                        if(numOfHued < 3){
                            ListUtils.splice(sd.actions,a,1);                        
                        }
                        else{
                            if(ac.state != "nop"){
                                int acscore = ac.score;
                                sd.score += ac.targets.size() * acscore * baseScore;
                                //扣掉目标方的分
                                for(int t = 0; t < ac.targets.size(); ++t){
                                    int six = ac.targets.get(t);
                                    game.gameSeats[six].score -= acscore * baseScore;
                                }                   
                            }   
                        }
                    }
                }
            }
        }
    }
    
    private void fnNoticeResult(String userId, Room roomInfo, List<UserRT> results, boolean isEnd){
        List<Endinfo> endinfo = null;
        if(isEnd){
            endinfo = new ArrayList<>();
            for(int i = 0; i < roomInfo.seats.length; ++i){
                Seat rs = roomInfo.seats[i];
                endinfo.add(new Endinfo(rs));
            }   
        }
        
        Map<String, Object> ret = new HashMap<>();
        ret.put("results", results);
        ret.put("endinfo", endinfo);
        userMgr.broacastInRoom("game_over_push",ret,userId,true);
        //如果局数已够，则进行整体结算，并关闭房间
        if(isEnd){
            setTimeout(new TimerTask() {
                @Override
                public void run() {
                    if(roomInfo.numOfGames > 1){
                        store_history(roomInfo);    
                    }
                    userMgr.kickAllInRoom(roomInfo.id);
                    roomMgr.destroy(roomInfo.id);
                    db.archive_games(roomInfo.uuid);            
                }
            }, 1500);
        }
    }

    public void doGameOver(NoGame game, String userId, boolean forceEnd){
        String roomId = roomMgr.getUserRoom(userId);
        if(roomId == null){
            return;
        }
        Room roomInfo = roomMgr.getRoom(roomId);
        if(roomInfo == null){
            return;
        }

        List<UserRT> results = new ArrayList<>();
        int[] dbresult = {0,0,0,0};
        
        

        if(game != null){
            if(!forceEnd){
                calculateResult(game,roomInfo);    
            }
           
            for(int i = 0; i < roomInfo.seats.length; ++i){
                Seat rs = roomInfo.seats[i];
                NoSeat sd = game.gameSeats[i];

                rs.ready = false;
                rs.score += sd.score;
                rs.numZiMo += sd.numZiMo;
                rs.numJiePao += sd.numJiePao;
                rs.numDianPao += sd.numDianPao;
                rs.numAnGang += sd.numAnGang;
                rs.numMingGang += sd.numMingGang;
                rs.numChaJiao += sd.numChaJiao;
                
                UserRT userRT = new UserRT(rs, sd, game.hupaiList.indexOf(i));
                
                for(NoAction k : sd.actions){
                    userRT.actions.add(new NoAction(k.type));
                }
                results.add(userRT);


                dbresult[i] = sd.score;
                gameSeatsOfUsers.remove(sd.userId);
            }
            games.remove(roomId);
            
            int old = roomInfo.nextButton;
            if(game.yipaoduoxiang >= 0){
                roomInfo.nextButton = game.yipaoduoxiang;
            }
            else if(game.firstHupai >= 0){
                roomInfo.nextButton = game.firstHupai;
            }
            else{
                roomInfo.nextButton = (game.turn + 1) % 4;
            }

            if(old != roomInfo.nextButton){
                db.update_next_button(roomId,roomInfo.nextButton);
            }
        }
        
        if(forceEnd || game == null){
            fnNoticeResult(userId, roomInfo, results, true); 
        }
        else{
            //保存游戏
            store_game(game);
            db.update_game_result(roomInfo.uuid,game.gameIndex,dbresult);
            
            //记录打牌信息
            String str = Json.toJson(game.actionList);
            db.update_game_action_records(roomInfo.uuid,game.gameIndex,str);
                
            //保存游戏局数
            db.update_num_of_turns(roomId,roomInfo.numOfGames);
            
            //如果是第一次，并且不是强制解散 则扣除房卡
            if(roomInfo.numOfGames == 1){
                int cost = 2;
                if(roomInfo.conf.maxGames == 8){
                    cost = 3;
                }
                db.cost_gems(game.gameSeats[0].userId,cost);
            }
                
            boolean isEnd = (roomInfo.numOfGames >= roomInfo.conf.maxGames);
            fnNoticeResult(userId, roomInfo, results, isEnd);
        }
    }

    public NoAction recordUserAction(NoGame game, NoSeat seatData, String type, Object target){
        NoAction d = new NoAction(type);
        if(target != null){
            if(target instanceof Integer){
                d.targets.add((Integer)target);    
            } else {
                // TODO
                d.targets = (List<Integer>)target;
            }
        } else {
            for(int i = 0; i < game.gameSeats.length; ++i){
                NoSeat s = game.gameSeats[i];
                if(i != seatData.seatIndex && s.hued == false){
                    d.targets.add(i);
                }
            }        
        }

        seatData.actions.add(d);
        return d;
    }

    public void recordGameAction(NoGame game, int si, int action, Integer pai){
        game.actionList.add(si);
        game.actionList.add(action);
        if(pai != null){
            game.actionList.add(pai);
        }
    }

    @Override
    public void setReady(String userId){
        String roomId = roomMgr.getUserRoom(userId);
        if(roomId == null){
            return;
        }
        Room roomInfo = roomMgr.getRoom(roomId);
        if(roomInfo == null){
            return;
        }

        roomMgr.setReady(userId,true);

        NoGame game = games.get(roomId);
        if(game == null){
            if(roomInfo.seats.length == 4){
                for(int i = 0; i < roomInfo.seats.length; ++i){
                    Seat s = roomInfo.seats[i];
                    if(s.ready == false || userMgr.isOnline(s.userId)==false){
                        return;
                    }
                }
                //4个人到齐了，并且都准备好了，则开始新的一局
                begin(roomId);
            }
        }
        else{
            int numOfMJ = game.mahjongs.length - game.currentIndex;
            int remainingGames = roomInfo.conf.maxGames - roomInfo.numOfGames;

            
            List<Map<String, Object>> seats = new ArrayList<>();
            NoSeat seatData = null;
            for(int i = 0; i < 4; ++i){
                NoSeat sd = game.gameSeats[i];

                Map<String, Object> s = new HashMap<>();
                s.put("userid", sd.userId);
                s.put("folds", sd.folds);
                s.put("angangs", sd.angangs);
                s.put("diangangs", sd.diangangs);
                s.put("wangangs", sd.wangangs);
                s.put("pengs", sd.pengs);
                s.put("que", sd.que);
                s.put("hued", sd.hued);
                s.put("huinfo", sd.huInfo);
                s.put("iszimo", sd.iszimo);
                if(sd.userId.equals(userId)){
                    s.put("holds", sd.holds);
                    s.put("huanpais", sd.huanpais);
                    seatData = sd;
                }
                else{
                    s.put("huanpais", sd.huanpais != null ? new int[0] : null);
                }
                seats.add(s);
            }
            
            Map<String, Object> data = new HashMap<>();
            data.put("state", game.state);
            data.put("numofmj", numOfMJ);
            data.put("button", game.button);
            data.put("turn", game.turn);
            data.put("chuPai", game.chuPai);
            data.put("huanpaimethod", game.huanpaiMethod);
            data.put("seats", seats);
            //同步整个信息给客户端
            userMgr.sendMsg(userId,"game_sync_push",data);
            sendOperations(game, seatData, game.chuPai);
        }
    }

    public void store_single_history(String userId, NoHistory history){
        List<NoHistory> data = db.get_user_history(userId);
        if(data == null){
            data = new ArrayList<>();
        }
        while(data.size() >= 10){
            ListUtils.shift(data);
        }
        data.add(history);
        db.update_user_history(userId,data);
    }

    public void store_history(Room roomInfo){
        Seat[] seats = roomInfo.seats;
        NoHistory history = new NoHistory();
        history.uuid = roomInfo.uuid;
        history.id = roomInfo.id;
        history.time = roomInfo.createTime;
        history.seats = new NoHistorySeat[4];

        for(int i = 0; i < seats.length; ++i){
            Seat rs = seats[i];
            NoHistorySeat hs = new NoHistorySeat();
            hs.userid = rs.userId;
            hs.name = StrUtils.toBase64(rs.name);
            hs.score = rs.score;
            history.seats[i] = hs;
        }

        for(int i = 0; i < seats.length; ++i){
            Seat s = seats[i];
            store_single_history(s.userId,history);
        }
    }

    public void construct_game_base_info(NoGame game){
        Object[] game_seats = new Object[4];
        for(int i = 0; i < 4; ++i){
            game_seats[i] = game.gameSeats[i].holds;
        }
        
        Map<String, Object> baseInfo = new HashMap<>();
        baseInfo.put("type", game.conf.type);
        baseInfo.put("button", game.button);
        baseInfo.put("index", game.gameIndex);
        baseInfo.put("mahjongs", game.mahjongs);
        baseInfo.put("game_seats", game_seats);
        game.baseInfoJson = Json.toJson(baseInfo);
    }

    public void store_game(NoGame game) {
        db.create_game(game.roomInfo.uuid,game.gameIndex,game.baseInfoJson);
    }

    //开始新的一局
    public void begin(String roomId) {
        Room roomInfo = roomMgr.getRoom(roomId);
        if(roomInfo == null){
            return;
        }
        Seat[] seats = roomInfo.seats;

        NoGame game = new NoGame();
        game.conf = roomInfo.conf;
        game.roomInfo = roomInfo;
        game.gameIndex = roomInfo.numOfGames;

        game.button = roomInfo.nextButton;
        game.mahjongs = new int[108];
        game.currentIndex = 0;
        game.gameSeats = new NoSeat[4];

        game.numOfQue = 0;
        game.turn = 0;
        game.chuPai = -1;
        game.state = "idle";
        game.firstHupai = -1;
        game.yipaoduoxiang = -1;
        game.fangpaoshumu = -1;
        game.actionList = new ArrayList<>();
        game.hupaiList = new ArrayList<>();
        game.chupaiCnt = 0;
        
        roomInfo.numOfGames++;

        for(int i = 0; i < 4; ++i){
            NoSeat data = new NoSeat();

            data.game = game;

            data.seatIndex = i;

            data.userId = seats[i].userId;
            //持有的牌
            data.holds = new ArrayList<>();
            //打出的牌
            data.folds = new ArrayList<>();
            //暗杠的牌
            data.angangs = new ArrayList<>();
            //点杠的牌
            data.diangangs = new ArrayList<>();
            //弯杠的牌
            data.wangangs = new ArrayList<>();
            //碰了的牌
            data.pengs = new ArrayList<>();
            //缺一门
            data.que = -1;

            //换三张的牌
            data.huanpais = null;

            //玩家手上的牌的数目，用于快速判定碰杠
            data.countMap = new HashMap<>();
            //玩家听牌，用于快速判定胡了的番数
            data.tingMap = new HashMap<>();
            data.pattern = "";

            //是否可以杠
            data.canGang = false;
            //用于记录玩家可以杠的牌
            data.gangPai = new ArrayList<>();

            //是否可以碰
            data.canPeng = false;
            //是否可以胡
            data.canHu = false;
            //是否可以出牌
            data.canChuPai = false;

            //如果guoHuFan >=0 表示处于过胡状态，
            //如果过胡状态，那么只能胡大于过胡番数的牌
            data.guoHuFan = -1;

            //是否胡了
            data.hued = false;
            //是否是自摸
            data.iszimo = false;

            data.isGangHu = false;

            //
            data.actions = new ArrayList<>();

            data.fan = 0;
            data.score = 0;
            data.lastFangGangSeat = -1;
            
            //统计信息
            data.numZiMo = 0;
            data.numJiePao = 0;
            data.numDianPao = 0;
            data.numAnGang = 0;
            data.numMingGang = 0;
            data.numChaJiao = 0;
            game.gameSeats[i] = data;
            gameSeatsOfUsers.put(data.userId, data);
        }
        games.put(roomId, game);
        //洗牌
        shuffle(game);
        //发牌
        deal(game);

        

        int numOfMJ = game.mahjongs.length - game.currentIndex;
        boolean huansanzhang = roomInfo.conf.hsz;

        for(int i = 0; i < seats.length; ++i){
            //开局时，通知前端必要的数据
            Seat s = seats[i];
            //通知玩家手牌
            userMgr.sendMsg(s.userId,"game_holds_push",game.gameSeats[i].holds);
            //通知还剩多少张牌
            userMgr.sendMsg(s.userId,"mj_count_push",numOfMJ);
            //通知还剩多少局
            userMgr.sendMsg(s.userId,"game_num_push",roomInfo.numOfGames);
            //通知游戏开始
            userMgr.sendMsg(s.userId,"game_begin_push",game.button);

            if(huansanzhang == true){
                game.state = "huanpai";
                //通知准备换牌
                userMgr.sendMsg(s.userId,"game_huanpai_push", null);
            }
            else{
                game.state = "dingque";
                //通知准备定缺
                userMgr.sendMsg(s.userId,"game_dingque_push", null);
            }
        }
    }
    
    //换牌函数
    private void huanSanZhangFn(NoSeat s1, int[] huanjin){
        for(int i = 0; i < huanjin.length; ++i){
            int p = huanjin[i];
            s1.holds.add(p);
            if(s1.countMap.get(p) == null){
                s1.countMap.put(p, 0);    
            }
            s1.countMap.put(p, s1.countMap.get(p) + 1);
        }
    }

    @Override
    public void huanSanZhang(String userId,int p1,int p2,int p3){
        NoSeat seatData = gameSeatsOfUsers.get(userId);
        if(seatData == null){
            System.out.println("can't find user game data.");
            return;
        }

        NoGame game = seatData.game;
        if(!"huanpai".equals(game.state)){
            System.out.println("can't recv huansanzhang when game.state == " + game.state);
            return;
        }

        if(seatData.huanpais != null){
            System.out.println("player has done this action.");
            return;
        }

        if(seatData.countMap.get(p1) == null || seatData.countMap.get(p1).intValue() == 0){
            return;
        }
        seatData.countMap.put(p1, seatData.countMap.get(p1)-1);

        if(seatData.countMap.get(p2) == null || seatData.countMap.get(p2).intValue() == 0){
            seatData.countMap.put(p1, seatData.countMap.get(p1)+1);
            return;
        }
        seatData.countMap.put(p2, seatData.countMap.get(p2)-1);

        if(seatData.countMap.get(p3) == null || seatData.countMap.get(p3).intValue() == 0){
            seatData.countMap.put(p1, seatData.countMap.get(p1)+1);
            seatData.countMap.put(p2, seatData.countMap.get(p2)+1);
            return;
        }

        seatData.countMap.put(p1, seatData.countMap.get(p1)+1);
        seatData.countMap.put(p2, seatData.countMap.get(p2)+1);

        seatData.huanpais = new int[]{p1,p2,p3};
        
        for(int i = 0; i < seatData.huanpais.length; ++i){
            int p = seatData.huanpais[i];
            int idx = seatData.holds.indexOf(p);
            ListUtils.splice(seatData.holds, idx,1);
            seatData.countMap.put(p, seatData.countMap.get(p)-1);
        }
        userMgr.sendMsg(seatData.userId,"game_holds_push",seatData.holds);
        
        for(int i = 0; i < game.gameSeats.length; ++i){
            NoSeat sd = game.gameSeats[i];
            if(sd == seatData){
                Map<String, Object> rd = new HashMap<>();
                rd.put("si", seatData.userId);
                rd.put("huanpais", seatData.huanpais);
                userMgr.sendMsg(sd.userId,"huanpai_notify",rd);            
            }
            else{
                Map<String, Object> rd = new HashMap<>();
                rd.put("si", seatData.userId);
                rd.put("huanpais", new int[0]);
                userMgr.sendMsg(sd.userId,"huanpai_notify",rd);
            }
        }

        //如果还有未换牌的玩家，则继承等待
        for(int i = 0; i < game.gameSeats.length; ++i){
            if(game.gameSeats[i].huanpais == null){
                return;
            }
        }


       

        //开始换牌
        double f = Math.random();
        NoSeat[] s = game.gameSeats;
        int huanpaiMethod = 0;
        //对家换牌
        if(f < 0.33){
            huanSanZhangFn(s[0],s[2].huanpais);
            huanSanZhangFn(s[1],s[3].huanpais);
            huanSanZhangFn(s[2],s[0].huanpais);
            huanSanZhangFn(s[3],s[1].huanpais);
            huanpaiMethod = 0;
        }
        //换下家的牌
        else if(f < 0.66){
            huanSanZhangFn(s[0],s[1].huanpais);
            huanSanZhangFn(s[1],s[2].huanpais);
            huanSanZhangFn(s[2],s[3].huanpais);
            huanSanZhangFn(s[3],s[0].huanpais);
            huanpaiMethod = 1;
        }
        //换上家的牌
        else{
            huanSanZhangFn(s[0],s[3].huanpais);
            huanSanZhangFn(s[1],s[0].huanpais);
            huanSanZhangFn(s[2],s[1].huanpais);
            huanSanZhangFn(s[3],s[2].huanpais);
            huanpaiMethod = 2;
        }
        
        Map<String, Object> rd = new HashMap<>();
        rd.put("method", huanpaiMethod);
        game.huanpaiMethod = huanpaiMethod;

        game.state = "dingque";
        for(int i = 0; i < s.length; ++i){
            userMgr.sendMsg(s[i].userId,"game_huanpai_over_push",rd);

            userMgr.sendMsg(s[i].userId,"game_holds_push",s[i].holds);
            //通知准备定缺
            userMgr.sendMsg(s[i].userId,"game_dingque_push", null);
        }
    }

    public void dingQue(String userId, int type){
        NoSeat seatData = gameSeatsOfUsers.get(userId);
        if(seatData == null){
            System.out.println("can't find user game data.");
            return;
        }

        NoGame game = seatData.game;
        if(!"dingque".equals(game.state)){
            System.out.println("can't recv dingQue when game.state == " + game.state);
            return;
        }

        if(seatData.que < 0){
            game.numOfQue++;    
        }

        seatData.que = type;
        

        //检查玩家可以做的动作
        //如果4个人都定缺了，通知庄家出牌
        if(game.numOfQue == 4){
            construct_game_base_info(game);
            int[] arr = {1,1,1,1};
            for(int i = 0; i < game.gameSeats.length; ++i){
                arr[i] = game.gameSeats[i].que;
            }
            userMgr.broacastInRoom("game_dingque_finish_push",arr,seatData.userId,true);
            userMgr.broacastInRoom("game_playing_push",null,seatData.userId,true);

            //进行听牌检查
            for(int i = 0; i < game.gameSeats.length; ++i){
                int duoyu = -1;
                NoSeat gs = game.gameSeats[i];
                if(gs.holds.size() == 14){
                    duoyu = ListUtils.pop(gs.holds);
                    gs.countMap.put(duoyu, gs.countMap.get(duoyu) - 1);
                }
                checkCanTingPai(game,gs);
                if(duoyu >= 0){
                    gs.holds.add(duoyu);
                    gs.countMap.put(duoyu, gs.countMap.get(duoyu) + 1);
                }
            }
            
            NoSeat turnSeat = game.gameSeats[game.turn];
            game.state = "playing";
            //通知玩家出牌方
            turnSeat.canChuPai = true;
            userMgr.broacastInRoom("game_chupai_push",turnSeat.userId,turnSeat.userId,true);
            //检查是否可以暗杠或者胡
            //直杠
            checkCanAnGang(game,turnSeat);
            //检查胡 用最后一张来检查
            checkCanHu(game,turnSeat,turnSeat.holds.get(turnSeat.holds.size() - 1));
            //通知前端
            sendOperations(game,turnSeat,game.chuPai);
        }
        else{
            userMgr.broacastInRoom("game_dingque_notify_push",seatData.userId,seatData.userId,true);
        }
    }

    @Override
    public void chuPai(String userId, int pai){
        NoSeat seatData = gameSeatsOfUsers.get(userId);
        if(seatData == null){
            System.out.println("can't find user game data.");
            return;
        }

        NoGame game = seatData.game;
        int seatIndex = seatData.seatIndex;
        //如果不该他出，则忽略
        if(game.turn != seatData.seatIndex){
            System.out.println("not your turn.");
            return;
        }

        if(seatData.hued){
            System.out.println("you have already hued. no kidding plz.");
            return;
        }

        if(seatData.canChuPai == false){
            System.out.println("no need chupai.");
            return;
        }

        if(hasOperations(seatData)){
            System.out.println("plz guo before you chupai.");
            return;
        }

        //从此人牌中扣除
        int index = seatData.holds.indexOf(pai);
        if(index == -1){
            System.out.println("holds:" + seatData.holds);
            System.out.println("can't find mj." + pai);
            return;
        }
        
        seatData.canChuPai = false;
        game.chupaiCnt ++;
        seatData.guoHuFan = -1;
        
        ListUtils.splice(seatData.holds, index,1);
        seatData.countMap.put(pai, seatData.countMap.get(pai) - 1);
        game.chuPai = pai;
        recordGameAction(game, seatData.seatIndex,ACTION_CHUPAI,pai);
        checkCanTingPai(game, seatData);
       
        Map<String, Object> ret = new HashMap<>();
        ret.put("userId", seatData.userId);
        ret.put("pai", pai);
        userMgr.broacastInRoom("game_chupai_notify_push",ret,seatData.userId,true);

        //如果出的牌可以胡，则算过胡
        if(seatData.tingMap.containsKey(game.chuPai)){
            seatData.guoHuFan = seatData.tingMap.get(game.chuPai).fan;
        }
        
        //检查是否有人要胡，要碰 要杠
        boolean hasActions = false;
        for(int i = 0; i < game.gameSeats.length; ++i){
            //玩家自己不检查
            if(game.turn == i){
                continue;
            }
            NoSeat ddd = game.gameSeats[i];
            //已经和牌的不再检查
            if(ddd.hued){
                continue;
            }

            checkCanHu(game,ddd,pai);
            if(seatData.lastFangGangSeat == -1){
                if(ddd.canHu && ddd.guoHuFan >= 0 && ddd.tingMap.get(pai).fan <= ddd.guoHuFan){
                    System.out.println("ddd.guoHuFan:" + ddd.guoHuFan);
                    ddd.canHu = false;
                    userMgr.sendMsg(ddd.userId,"guohu_push",null);            
                }     
            }
            checkCanPeng(game,ddd,pai);
            checkCanDianGang(game,ddd,pai);
            if(hasOperations(ddd)){
                sendOperations(game,ddd,game.chuPai);
                hasActions = true;    
            }
        }
        
        //如果没有人有操作，则向下一家发牌，并通知他出牌
        if(!hasActions){
            setTimeout(new TimerTask() {
                @Override
                public void run() {
                    Map<String, Object> msg = new HashMap<>();
                    msg.put("userId", seatData.userId);
                    msg.put("pai", game.chuPai);
                    userMgr.broacastInRoom("guo_notify_push",msg,seatData.userId,true);
                    seatData.folds.add(game.chuPai);
                    game.chuPai = -1;
                    moveToNextUser(game, null);
                    doUserMoPai(game);
                }
            }, 500);
        }
    }

    @Override
    public void peng(String userId){
        NoSeat seatData = gameSeatsOfUsers.get(userId);
        if(seatData == null){
            System.out.println("can't find user game data.");
            return;
        }

        NoGame game = seatData.game;

        //如果是他出的牌，则忽略
        if(game.turn == seatData.seatIndex){
            System.out.println("it's your turn.");
            return;
        }

        //如果没有碰的机会，则不能再碰
        if(seatData.canPeng == false){
            System.out.println("seatData.peng == false");
            return;
        }

        //和的了，就不要再来了
        if(seatData.hued){
            System.out.println("you have already hued. no kidding plz.");
            return;
        }
        
        //如果有人可以胡牌，则需要等待
        int it = game.turn;
        while(true){
            it = (it + 1)%4;
            if(it == game.turn){
                break;
            }
            else{
                NoSeat ddd = game.gameSeats[it];
                if(ddd.canHu && it != seatData.seatIndex){
                    return;    
                }
            }
        }


        clearAllOptions(game, null);

        //验证手上的牌的数目
        int pai = game.chuPai;
        Integer c = seatData.countMap.get(pai);
        if(c == null || c < 2){
            System.out.println("pai:" + pai + ",count:" + c);
            System.out.println(seatData.holds);
            System.out.println("lack of mj.");
            return;
        }

        //进行碰牌处理
        //扣掉手上的牌
        //从此人牌中扣除
        for(int i = 0; i < 2; ++i){
            int index = seatData.holds.indexOf(pai);
            if(index == -1){
                System.out.println("can't find mj.");
                return;
            }
            ListUtils.splice(seatData.holds, index,1);
            seatData.countMap.put(pai, seatData.countMap.get(pai) - 1);
        }
        seatData.pengs.add(pai);
        game.chuPai = -1;

        recordGameAction(game, seatData.seatIndex,ACTION_PENG,pai);

        //广播通知其它玩家
        Map<String, Object> ret = new HashMap<>();
        ret.put("userId", seatData.userId);
        ret.put("pai", pai);
        userMgr.broacastInRoom("peng_notify_push", ret,seatData.userId,true);

        //碰的玩家打牌
        moveToNextUser(game, seatData.seatIndex);
        
        //广播通知玩家出牌方
        seatData.canChuPai = true;
        userMgr.broacastInRoom("game_chupai_push",seatData.userId,seatData.userId,true);
    }

    public boolean isPlaying(String userId){
        NoSeat seatData = gameSeatsOfUsers.get(userId);
        if(seatData == null){
            return false;
        }

        NoGame game = seatData.game;

        if("idle".equals(game.state)){
            return false;
        }
        return true;
    }

    public boolean checkCanQiangGang(NoGame game, NoSeat turnSeat, NoSeat seatData, int pai){
        boolean hasActions = false;
        for(int i = 0; i < game.gameSeats.length; ++i){
            //杠牌者不检查
            if(seatData.seatIndex == i){
                continue;
            }
            NoSeat ddd = game.gameSeats[i];
            //已经和牌的不再检查
            if(ddd.hued){
                continue;
            }

            checkCanHu(game,ddd,pai);
            if(ddd.canHu){
                sendOperations(game,ddd,pai);
                hasActions = true;
            }
        }
        if(hasActions){
            game.qiangGangContext = new QiangGangContext();
            game.qiangGangContext.turnSeat = turnSeat;
            game.qiangGangContext.seatData = seatData;
            game.qiangGangContext.pai = pai;
            game.qiangGangContext.isValid = true;
        }
        else{
            game.qiangGangContext = null;
        }
        return game.qiangGangContext != null;
    }

    public void doGang(NoGame game,NoSeat turnSeat, NoSeat seatData, String gangtype,int numOfCnt,int pai){
        int seatIndex = seatData.seatIndex;
        int gameTurn = turnSeat.seatIndex;
        
        boolean isZhuanShouGang = false;
        if("wangang".equals(gangtype)){
            int idx = seatData.pengs.indexOf(pai);
            if(idx >= 0){
                ListUtils.splice(seatData.pengs, idx,1);
            }
            
            //如果最后一张牌不是杠的牌，则认为是转手杠
            if(seatData.holds.get(seatData.holds.size() - 1).intValue() != pai){
                isZhuanShouGang = true;
            }
        }
        //进行碰牌处理
        //扣掉手上的牌
        //从此人牌中扣除
        for(int i = 0; i < numOfCnt; ++i){
            int index = seatData.holds.indexOf(pai);
            if(index == -1){
                System.out.println(seatData.holds);
                System.out.println("can't find mj.");
                return;
            }
            ListUtils.splice( seatData.holds, index,1);
            seatData.countMap.put(pai, seatData.countMap.get(pai) - 1);
        }

        recordGameAction(game, seatData.seatIndex,ACTION_GANG,pai);

        //记录下玩家的杠牌
        if("angang".equals(gangtype)) {
            seatData.angangs.add(pai);
            NoAction ac = recordUserAction(game,seatData,"angang", null);
            ac.score = game.conf.baseScore*2;
        } else if("diangang".equals(gangtype)) {
            seatData.diangangs.add(pai);
            NoAction ac = recordUserAction(game, seatData,"diangang",gameTurn);
            ac.score = game.conf.baseScore*2;
            NoSeat fs = turnSeat;
            recordUserAction(game,fs,"fanggang",seatIndex);
        } else if("wangang".equals(gangtype)) {
            seatData.wangangs.add(pai);
            if(isZhuanShouGang == false){
                NoAction ac = recordUserAction(game,seatData,"wangang", null);
                ac.score = game.conf.baseScore;            
            }
            else{
                recordUserAction(game,seatData,"zhuanshougang", null);
            }
        }

        checkCanTingPai(game, seatData);
        //通知其他玩家，有人杠了牌
        Map<String, Object> ret = new HashMap<>();
        ret.put("userid", seatData.userId);
        ret.put("pai", pai);
        ret.put("gangtype", gangtype);
        userMgr.broacastInRoom("gang_notify_push",ret,seatData.userId,true);
        
        //变成自己的轮子
        moveToNextUser(game,seatIndex);
        //再次摸牌
        doUserMoPai(game);   
        
        //只能放在这里。因为过手就会清除杠牌标记
        seatData.lastFangGangSeat = gameTurn;
    }

    @Override
    public void gang(String userId, int pai){
        NoSeat seatData = gameSeatsOfUsers.get(userId);
        if(seatData == null){
            System.out.println("can't find user game data.");
            return;
        }

        int seatIndex = seatData.seatIndex;
        NoGame game = seatData.game;

        //如果没有杠的机会，则不能再杠
        if(seatData.canGang == false) {
            System.out.println("seatData.gang == false");
            return;
        }

        //和的了，就不要再来了
        if(seatData.hued){
            System.out.println("you have already hued. no kidding plz.");
            return;
        }

        if(seatData.gangPai.indexOf(pai) == -1){
            System.out.println("the given pai can't be ganged.");
            return;   
        }
        
        //如果有人可以胡牌，则需要等待
        int i = game.turn;
        while(true){
            i = (i + 1)%4;
            if(i == game.turn){
                break;
            }
            else{
                NoSeat ddd = game.gameSeats[i];
                if(ddd.canHu && i != seatData.seatIndex){
                    return;    
                }
            }
        }

        int numOfCnt = seatData.countMap.get(pai);

        String gangtype = "";
        //弯杠 去掉碰牌
        if(numOfCnt == 1){
            gangtype = "wangang";
        }
        else if(numOfCnt == 3){
            gangtype = "diangang";
        }
        else if(numOfCnt == 4){
            gangtype = "angang";
        }
        else{
            System.out.println("invalid pai count.");
            return;
        }
        
        game.chuPai = -1;
        clearAllOptions(game, null);
        seatData.canChuPai = false;
        
        userMgr.broacastInRoom("hangang_notify_push",seatIndex,seatData.userId,true);
        
        //如果是弯杠，则需要检查是否可以抢杠
        NoSeat turnSeat = game.gameSeats[game.turn];
        if(numOfCnt == 1){
            boolean canQiangGang = checkCanQiangGang(game,turnSeat,seatData,pai);
            if(canQiangGang){
                return;
            }
        }
        
        doGang(game,turnSeat,seatData,gangtype,numOfCnt,pai);
    }

    @Override
    public void hu(String userId){
        NoSeat seatData = gameSeatsOfUsers.get(userId);
        if(seatData == null){
            System.out.println("can't find user game data.");
            return;
        }

        int seatIndex = seatData.seatIndex;
        NoGame game = seatData.game;

        //如果他不能和牌，那和个啥啊
        if(seatData.canHu == false){
            System.out.println("invalid request.");
            return;
        }

        //和的了，就不要再来了
        if(seatData.hued){
            System.out.println("you have already hued. no kidding plz.");
            return;
        }

        //标记为和牌
        seatData.hued = true;
        int hupai = game.chuPai;
        boolean isZimo = false;

        NoSeat turnSeat = game.gameSeats[game.turn];
        seatData.isGangHu = turnSeat.lastFangGangSeat >= 0;
        int notify = -1;
        
        if(game.qiangGangContext != null){
            NoSeat gangSeat = game.qiangGangContext.seatData;
            hupai = game.qiangGangContext.pai;
            notify = hupai;
            NoAction ac = recordUserAction(game, seatData,"qiangganghu",gangSeat.seatIndex);    
            ac.iszimo = false;
            recordGameAction(game,seatIndex,ACTION_HU,hupai);
            seatData.isQiangGangHu = true;
            game.qiangGangContext.isValid = false;
            
            
            int idx = gangSeat.holds.indexOf(hupai);
            if(idx != -1){
                ListUtils.splice(gangSeat.holds, idx,1);
                gangSeat.countMap.put(hupai, gangSeat.countMap.get(hupai) - 1);
                userMgr.sendMsg(gangSeat.userId,"game_holds_push",gangSeat.holds);
            }
            //将牌添加到玩家的手牌列表，供前端显示
            seatData.holds.add(hupai);
            if(seatData.countMap.containsKey(hupai)){
                seatData.countMap.put(hupai, seatData.countMap.get(hupai) + 1);
            }
            else{
                seatData.countMap.put(hupai, 1);
            }
            
            recordUserAction(game,gangSeat,"beiqianggang",seatIndex);
        }
        else if(game.chuPai == -1){
            hupai = seatData.holds.get(seatData.holds.size() - 1);
            notify = -1;
            if(seatData.isGangHu){
                if(turnSeat.lastFangGangSeat == seatIndex){
                    NoAction ac = recordUserAction(game, seatData,"ganghua", null);    
                    ac.iszimo = true;
                }
                else{
                    boolean diangganghua_zimo = game.conf.dianganghua == 1;
                    if(diangganghua_zimo){
                        NoAction ac = recordUserAction(game, seatData,"dianganghua", null);
                        ac.iszimo = true;
                    }
                    else{
                        NoAction ac = recordUserAction(game, seatData,"dianganghua",turnSeat.lastFangGangSeat);
                        ac.iszimo = false;
                    }
                }
            }
            else{
                NoAction ac = recordUserAction(game, seatData,"zimo", null);
                ac.iszimo = true;
            }

            isZimo = true;
            recordGameAction(game,seatIndex,ACTION_ZIMO,hupai);
        }
        else{
            notify = game.chuPai;
            //将牌添加到玩家的手牌列表，供前端显示
            seatData.holds.add(game.chuPai);
            if(seatData.countMap.containsKey(game.chuPai)){
                seatData.countMap.put(game.chuPai, seatData.countMap.get(game.chuPai) + 1);
            }
            else{
                seatData.countMap.put(game.chuPai, 1);
            }

            System.out.println(seatData.holds);

            String at = "hu";
            //炮胡
            if(turnSeat.lastFangGangSeat >= 0){
                at = "gangpaohu";
            }

            NoAction ac = recordUserAction(game, seatData,at,game.turn);
            ac.iszimo = false;

            //毛转雨
            if(turnSeat.lastFangGangSeat >= 0){
                for(int i = turnSeat.actions.size()-1; i >= 0; --i){
                    NoAction t = turnSeat.actions.get(i);
                    if("diangang".equals(t.type) || "wangang".equals(t.type) || "angang".equals(t.type)){
                        t.state = "nop";
                        t.payTimes = 0;

                        NoAction nac = new NoAction("maozhuanyu");
                        nac.owner = turnSeat;
                        nac.ref = t;
                        seatData.actions.add(nac);
                        break;
                    }
                }
            }

            //记录玩家放炮信息
            NoSeat fs = game.gameSeats[game.turn];
            recordUserAction(game,fs,"fangpao",seatIndex);

            recordGameAction(game,seatIndex,ACTION_HU,hupai);

            game.fangpaoshumu++;

            if(game.fangpaoshumu > 1){
                game.yipaoduoxiang = seatIndex;
            }
        }

        if(game.firstHupai < 0){
            game.firstHupai = seatIndex;
        }

        //保存番数
        NoTing ti = seatData.tingMap.get(hupai);
        seatData.fan = ti.fan;
        seatData.pattern = ti.pattern;
        seatData.iszimo = isZimo;
        //如果是最后一张牌，则认为是海底胡
        seatData.isHaiDiHu = game.currentIndex == game.mahjongs.length;
        game.hupaiList.add(seatData.seatIndex);
        
        if(game.conf.tiandihu){
            if(game.chupaiCnt == 0 && game.button == seatData.seatIndex && game.chuPai == -1){
                seatData.isTianHu = true;
            }
            else if(game.chupaiCnt == 1 && game.turn == game.button && game.button != seatData.seatIndex && game.chuPai != -1){
                seatData.isDiHu = true;   
            }   
        }

        clearAllOptions(game, seatData);

        //通知前端，有人和牌了
        Map<String, Object> ret = new HashMap<>();
        ret.put("seatindex", seatIndex);
        ret.put("iszimo", isZimo);
        ret.put("hupai", notify);
        userMgr.broacastInRoom("hu_push",ret,seatData.userId,true);
        
        //
        if(game.lastHuPaiSeat == -1){
            game.lastHuPaiSeat = seatIndex;
        }
        else{
            int lp = (game.lastFangGangSeat - game.turn + 4) % 4;
            int cur = (seatData.seatIndex - game.turn + 4) % 4;
            if(cur > lp){
                game.lastHuPaiSeat = seatData.seatIndex;
            }
        }

        //如果只有一家没有胡，则结束
        int numOfHued = 0;
        for(int i = 0; i < game.gameSeats.length; ++i){
            NoSeat ddd = game.gameSeats[i];
            if(ddd.hued){
                numOfHued ++;
            }
        }
        //和了三家
        if(numOfHued == 3){
            doGameOver(game, seatData.userId, false);
            return;
        }

        //清空所有非胡牌操作
        for(int i = 0; i < game.gameSeats.length; ++i){
            NoSeat ddd = game.gameSeats[i];
            ddd.canPeng = false;
            ddd.canGang = false;
            ddd.canChuPai = false;
            sendOperations(game,ddd,hupai);
        }

        //如果还有人可以胡牌，则等待
        for(int i = 0; i < game.gameSeats.length; ++i){
            NoSeat ddd = game.gameSeats[i];
            if(ddd.canHu){
                return;
            }
        }
        
        //和牌的下家继续打
        clearAllOptions(game, null);
        game.turn = game.lastHuPaiSeat;
        moveToNextUser(game, null);
        doUserMoPai(game);
    }

    @Override
    public void guo(String userId){
        NoSeat seatData = gameSeatsOfUsers.get(userId);
        if(seatData == null){
            System.out.println("can't find user game data.");
            return;
        }

        int seatIndex = seatData.seatIndex;
        NoGame game = seatData.game;

        //如果玩家没有对应的操作，则也认为是非法消息
        if((seatData.canGang || seatData.canPeng || seatData.canHu) == false){
            System.out.println("no need guo.");
            return;
        }

        //如果是玩家自己的轮子，不是接牌，则不需要额外操作
        boolean doNothing = game.chuPai == -1 && game.turn == seatIndex;

        userMgr.sendMsg(seatData.userId,"guo_result", null);
        clearAllOptions(game, seatData);
        
        //这里还要处理过胡的情况
        if(game.chuPai >= 0 && seatData.canHu){
            seatData.guoHuFan = seatData.tingMap.get(game.chuPai).fan;
        }

        if(doNothing){
            return;
        }
        
        //如果还有人可以操作，则等待
        for(int i = 0; i < game.gameSeats.length; ++i){
            NoSeat ddd = game.gameSeats[i];
            if(hasOperations(ddd)){
                return;
            }
        }

        //如果是已打出的牌，则需要通知。
        if(game.chuPai >= 0){
            String uid = game.gameSeats[game.turn].userId;
            Map<String, Object> ret = new HashMap<>();
            ret.put("userId", uid);
            ret.put("pai", game.chuPai);
            userMgr.broacastInRoom("guo_notify_push",ret,seatData.userId,true);
            seatData.folds.add(game.chuPai);
            game.chuPai = -1;
        }
        
        
        QiangGangContext qiangGangContext = game.qiangGangContext;
        //清除所有的操作
        clearAllOptions(game, null);
        
        if(qiangGangContext != null && qiangGangContext.isValid){
            doGang(game,qiangGangContext.turnSeat,qiangGangContext.seatData,"wangang",1,qiangGangContext.pai);        
        }
        else{
            //下家摸牌
            moveToNextUser(game, null);
            doUserMoPai(game);   
        }
    }

    @Override
    public boolean hasBegan(String roomId){
        NoGame game = games.get(roomId);
        if(game != null){
            return true;
        }
        Room roomInfo = roomMgr.getRoom(roomId);
        if(roomInfo != null){
            return roomInfo.numOfGames > 0;
        }
        return false;
    }


    

    @Override
    public void doDissolve(String roomId){
        Room roomInfo = roomMgr.getRoom(roomId);
        if(roomInfo == null){
            return;
        }

        NoGame game = games.get(roomId);
        doGameOver(game,roomInfo.seats[0].userId,true);
    }

    @Override
    public Room dissolveRequest(String roomId, String userId){
        Room roomInfo = roomMgr.getRoom(roomId);
        if(roomInfo == null){
            return null;
        }

        if(roomInfo.dr != null){
            return null;
        }

        Integer seatIndex = roomMgr.getUserSeat(userId);
        if(seatIndex == null){
            return null;
        }

        roomInfo.dr = new RoomDr();
        roomInfo.dr.endTime = System.currentTimeMillis() + 30000;
        roomInfo.dr.states = new boolean[]{false,false,false,false};
        roomInfo.dr.states[seatIndex] = true;
        dissolvingList.add(roomId);
        return roomInfo;
    }

    @Override
    public Room dissolveAgree(String roomId,String userId,boolean agree){
        Room roomInfo = roomMgr.getRoom(roomId);
        if(roomInfo == null){
            return null;
        }

        if(roomInfo.dr == null){
            return null;
        }

        Integer seatIndex = roomMgr.getUserSeat(userId);
        if(seatIndex == null){
            return null;
        }

        if(agree){
            roomInfo.dr.states[seatIndex] = true;
        }
        else{
            roomInfo.dr = null;
            int idx = dissolvingList.indexOf(roomId);
            if(idx != -1){
                ListUtils.splice(dissolvingList, idx,1);     
            }
        }
        return roomInfo;
    }

    
    
    
//    /*
//    int mokgame = {
//        gameSeats:[{folds:[]}],
//        mahjongs:[],
//        currentIndex:-1,
//        conf:{
//            wz_yaojidai:2,
//        }
//    }
//    int mokseat = {
//        holds:[9,9,9,9,1,2,3,3,4,5,18,18,18,18],
//        isBaoTing:true,
//        countMap:{},
//        pengs:[],
//        feis:[],
//        diangangs:[],
//        angangs:[],
//        wangangs:[],
//        diansuos:[],
//        wansuos:[],
//        ansuos:[],
//        gangPai:[]
//    }
//
//    for(int k : mokseat.holds){
//        int pai = mokseat.holds[k];
//        if(mokseat.countMap.get(pai)){
//            mokseat.countMap.get(pai) ++;
//        }
//        else{
//            mokseat.countMap.get(pai) = 1;
//        }
//    }
//    checkCanAnGang(mokgame,mokseat);
//    System.out.println(mokseat.gangPai);
//    */
}
